<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Array的变化检测练习2.1</title>
</head>
<body>
    <div>
        <h2>Array的变化检测练习2.1</h2>
    </div>
</body>
</html>

<script>
const arrayProto = Array.prototype
const arrayMehtods = Object.create(arrayProto)

;[
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse"
].forEach((method) => {
    const original = arrayProto[method]
    Object.defineProperty(arrayMehtods, method, {
        enumerable:false,
        configurable: true,
        writable: true,
        value: function(...args) {
            return original.apply(this, args)
        }
    })
})

const hasProto = '__proto__' in {}
const arrayKeys = Object.getOwnPropertyNames(arrayMehtods)

function protoAugment(value, methods) {
    value.__proto__ = methods
}

function copyAugment(value, methods, keys) {
    for(let i=0;i<keys.length;i++){
        def(value, keys[i], value[keys[i]])
    }
}

function def(obj, key, val) {
    Object.defineProperty(obj, key, {
        enumerable: false,
        configurable: true,
        writable: true,
        value: val
    }) 
}

function observe(value) {
    if(typeof value !== 'object') return
    let ob
    if(hasOwn(value, '__ob__') && value.__ob__ instanceof Observer){
        ob = value.__ob__
    } else {
        ob = new Observer(value)
    }
    return ob
}

class Dep {
    constructor() {
        this.subs = []
    }

    addSub(sub) {
        this.subs.push(sub)
    }

    depend() {
        if(window.target){
            this.addSub(window.target)
        }
    }

    notify() {
        let subs = this.subs
        for(let i=0;i<subs.length;i++){
            subs[i].update()
        }
    }
}



class Observer {
    constructor(value) {
        this.value = value
        this.dep = new Dep()
        def(value, '__ob__', this)
        
        if(Array.isArray(this.value)){
            hasProto?protoAugment(this.value, arrayMehtods):copyAugment(this.value, arrayMehtods, arrayKeys)
        }else{
            this.walk(this.value)
        }
    }

    walk() {

    }

    defineReactive(data, key, val) {
        let childOb = observe(val)
        let dep = new Dep()
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get() {
                dep.depend()
                if(childOb){
                    childOb.dep.depend()
                }
                return val
            },
            set(newVal) {
                if(newVal === val) return
                val = newVal
                dep.notify()
            }
        })
    }
}
</script>